/*
 * profiler.h
 *
 *  Created on: Nov 24, 2013
 *      Author: killerg
 */

#ifndef PROFILER_H_
#define PROFILER_H_

#include <unordered_map>
#include <functional>

typedef std::function<double()> timecb_t;
typedef std::function<bool(int id, bool*,double*,int)> commitcb_t;

template<int STAGE_NUM>
struct profiler_t {

    struct log_t {
        bool fin[STAGE_NUM];
        double timer[STAGE_NUM];
        log_t() {
            for (int i=0; i<STAGE_NUM; ++i) {
                timer[i] = -1;
                fin[i] = false;
            }
        }
    };

    bool init(commitcb_t commitcb = NULL, timecb_t timecb = NULL);
    void destroy();

    void start(int id, int stage);
    void finish(int id, int stage);
    void commit(int id);

    std::unordered_map<int, log_t*> log_map;
    timecb_t timecb;
    commitcb_t commitcb;

};


#include "share/util.h"
#include "share/clock.h"

template<int STAGE_NUM>
inline bool profiler_t<STAGE_NUM>::init(commitcb_t commitcb, timecb_t timecb) {
    if (timecb)
        this->timecb = timecb;
    else
        this->timecb = []()->double{ return my_clock_t::get_time(); };

    if (commitcb)
        this->commitcb = commitcb;
    else
        this->commitcb = [](int id, bool* started, double* timer, int num)->bool{
            std::string str = fmt_str("%d ", id);
            for (int i=0; i<num; ++i) {
                str += fmt_str("%d:%f ", i, timer[i]);
                timer[i] = 0;
            }
            str += '\n';
//            printf(str.c_str());

            return true;
        };

    return true;
}

template<int STAGE_NUM>
inline void profiler_t<STAGE_NUM>::destroy() {
    for (auto itr = log_map.begin(); itr != log_map.end(); ++itr) {
        safe_del(itr->second);
    }
    log_map.clear();
}

template<int STAGE_NUM>
inline void profiler_t<STAGE_NUM>::start(int id, int stage) {
    if (stage >= STAGE_NUM || stage < 0)
        return;

    log_t* log = NULL;
    auto itr = log_map.find(id);
    if (itr == log_map.end()) {
        auto log = new log_t;
        itr = log_map.emplace(id, log).first;
    }
    log = itr->second;

    log->timer[stage] = timecb();
    log->fin[stage] = false;
}

template<int STAGE_NUM>
inline void profiler_t<STAGE_NUM>::finish(int id, int stage) {
    if (stage >= STAGE_NUM || stage < 0)
        return;

    auto itr = log_map.find(id);
    if (itr == log_map.end())
        return;
    log_t* log = itr->second;

    log->timer[stage] = timecb() - log->timer[stage];
    log->fin[stage] = true;
}

template<int STAGE_NUM>
inline void profiler_t<STAGE_NUM>::commit(int id) {
    auto itr = log_map.find(id);
    if (itr == log_map.end())
        return;

    log_t* log = itr->second;
    for (int i=0; i<STAGE_NUM; ++i)
        if (log->timer[i] >= 0.0f && !log->fin[i])
            log->timer[i] = 999999.0f;

    if (commitcb)
        commitcb(itr->first, log->fin, log->timer, STAGE_NUM);

    log_map.erase(itr);
    safe_del(log);
}

#endif /* PROFILER_H_ */
